/** @define {boolean}
*/
var _PERFORMANCE = false;

/** @define {boolean}
*/
var _EDITOR = true;

var _sys = {
	performance:_PERFORMANCE,
	editor:_EDITOR
}

/** @param {array} parentList
 * @param {function} cons
 */
var _inherit = function(parentList, cons){
	var proto = cons.prototype;
	parentList.forEach(function(parent){
		for (k in parent.prototype){
			proto[k] = parent.prototype[k];
		}
	});
	/** @constructor */
	var ret = function(){
		var upval = this;
		parentList.forEach(function(parent){
			parent.call(upval);
		})
		cons.call(upval);
	};
	ret.prototype = proto;
	return ret;
}

var Serializable = function(className){
	var ret = function(){
	}
	ret.prototype._class = className;
	ret.prototype.serialize = function(){
	}
	ret.prototype.deserialize = function(){
	}
	return ret;
};

var Identifiable = (function(){
	var gid = 0;
	var objs = {};
	/** @constructor */
	var ret = function(){
		objs[gid] = this;
		this.id = gid++;
	}
	ret.getObj = function(id){
		return objs[id];
	}
	ret.prototype.delObj = function(){
		delete objs[this.id];
	}
	return ret;
})();

/** @constructor */
var FuncList = function(){
	this.list = [];
}
FuncList.prototype.push = function(f){
	this.list.push(f);
}
FuncList.prototype.invoke = function(scope,params){
	this.list.forEach(function(f){
		f.apply(scope, params);
	});
}
//this func does not consider local var inside funcs
FuncList.prototype.genCode = function(){
	var otherf;
	this.list.forEach(function(f){
		otherF += f.toString();
	});
	return eval('function(){' +
		otherf +
		'}'
	);
}
var Living = _inherit([Identifiable], (function(){
	var cons = new FuncList();
	var dess = new FuncList();
	/** @constructor */
	var ret = function(){
		cons.invoke(this);
	}
	ret.prototype.delObj = function(){
		Identifiable.prototype.delObj.call(this);
		dess.invoke(this);
	}
	ret.prototype.AddCon = function(con){
		cons.push(con);
	}
	ret.prototype.AddDes = function(des){
		dess.push(des);
	}
	return ret;
})());

/**
 *@param {Living} cons
 *@param {string} prop
 */
var ArrayProp = function(cons, prop){
	cons.prototype.AddCon(function(){
		var vals = {};
		this[prop] = {
			/** @param {Identifiable} obj
			 */
			push:function(obj){
				vals[obj.id] = obj;
			},
			/** @param {Identifiable} obj
			 */
			remove:function(obj){
				delete vals[obj.id];
			},
			get:function(){
				return vals;
			}
		};
	});
}


var StringList = function(){
	var val = {};
	var len = 0;
	var up = this;
	this.add = function(str){
		val[str] = true;
		len++;
	}
	this.remove = function(str){
		delete val[str];
		len--;
	}
	this.get = function(){
		return val;
	}
	this.length = function(){
		return len;
	}
	this.create = function(arr){
		arr.forEach(function(str){
			up.add(str);
		});
	}
};

/** @define {number}*/
var RAND_RANGE = 36*36*36;

var IdContainer = function(keypool){
	var val = {};
	var genid = function(){
		do{
			var key = Math.floor(Math.random() * RAND_RANGE);
		}while (key in keypool);
		return key;
	}
	this.add = function(item){
		item._aid = genid();	//--> an item can't be added into two IdContainers!
		val[item._aid] = item;
		keypool[item._aid] = item;
		return item._aid;//return the key for use
	}
	this.remove = function(item){
		delete val[item._aid];
		delete keypool[item._aid];
	}
	this.get = function(){
		return val;
	}
};
var KeyPool = function(){
	var pool = {};
	this.getPool = function(){
		return pool;
	}
	this.getItem = function(key){
		return pool[key];
	}
}

var DashList = (function(){
	var pool = {};
	this.add = function(dashDecl){
		pool[dashDecl.name] = dashDecl;
	}
	this.remove = function(dashDecl){
		delete pool[dashDecl.name];
	}
	this.get = function(){
		return pool;
	}
	this.getItem = function(dash_name){
		return pool[dash_name];
	}
})();
DashList.create = function(config){
	if (typeof(config) =='string'){
		return [config];
	}else if (config instanceof Array){
		return config;
	}else{
		//ERROR
	}
}

var ScalarObj = function(){
	this.name = '';
	//although is is a table, it has only 1 value
	this.dashes = {};//key: dash_name, val: dash_impl or true for reference!
	this.set = null;
	this.get = null;
}
ScalarObj.create = function(config){
	var ret = new ScalarObj();
	ret.name = config.name;
	var dash = NativeDashboard.create(config.dash);
	dash.setName(config.name);
	dash.host = ret;
	ret.dashes[ret.name] = dash;
	ret.set = config.setter;
	ret.get = config.getter;
	return ret;
}

var NativeDashboard = function(){
	this.name = '';
	this.host = null;//for standalone dash
	this.slots = {};//key: decl_id, val: NativeSlot
	this.chips = {};//key: decl_id, val: NavtiveChip
}
var DashPool = new DashList();

NativeDashboard.prototype.setName = function(dash_name){
	this.name = dash_name;
	DashPool.add(dash_name, this);
}
NativeDashboard.create = function(config){
	var slotchip_pool = {};
	var ret = new NativeDashboard();
	if (config.name!=undefined)
		ret.setName(config.name);
	var id =0;
	for (var k in config.slots){
		ret.slots[id] = NativeSlot.create(k, config.slots[k]);
		slotchip_pool[id] = ret.slots[id];
		id++;
	}
	for (var k in config.chips){
		ret.chips[id] = NativeChip.create(k, config.chips[k]);
		slotchip_pool[id] = ret.chips[id];
		id++;
	}
	ret.getItem = function(key){
		return slotchip_pool[key];
	}
}

var NativeSlot = function(){
	this.name ='';
	this.dashes = [];//list of accepted dashes
	this.is_valpush = false;
	this.set = null;//setter
	this.get = null;//getter
}

NativeSlot.create = function(key,config){
	var ret = new NativeSlot();
	ret.name = key;
	if (config instanceof Object){
		ret.dashes = DashList.create(config.dashes);
		if (config.is_valpush!=undefined) 
			ret.is_valpush = config.is_valpush;
		if (config.setter!=undefined)
			ret.set = config.setter;
		if (config.getter!=undefined)
			ret.get = config.getter;
	}else{
		ret.dashes = DashList.create(config);
	}
	return ret;
}

var NativeChip = function(){
	this.name = '';
	this.slots = {};
	this.impl = null;
}
NativeChip.create = function(key, config){
	var ret = new NativeChip();
	ret.name = key;
	var id=0;
	for (var k in config.slots){
		ret.slots[id++] = NativeSlot.create(k, config.slots[k]);//this nativeSlot should not have setter and getter!
	}
	ret.impl = config.impl;
}
NativeChip.prototype.getItem = function(key){
	return this.slots[key];
}

var NativeObj = function(){
	this.name='';//compulsory, to create alias of all dashboards
	this.dashes = new DashList();
	this.connections = {};//key: slot_id: val: connections
	this.nativeDash = {};//key:dash_name, val:nativeDash
}
NativeObj.prototype.getItem = function(key){
	return this.dashes.getItem(key);
}
NativeObj.prototype.create = function(){
	return null;//return the real object here, with default values, but configured for connections
}
NativeObj.prototype.destroy = function(){
}
NativeObj.prototype.getDesigner = function(){
	return null;//return the designer obj
}

/**
 *SlotInst is an instance of a SlotDecl (per object) or
 *NativeSlot (per object & connection)
 *SlotInst is created during program execution
*/
var SlotInst = function(){
	this.host = null;
	this.decl = null;//slot_id inside proto
	this.setter = null;
	this.getter = null;
}


var DashboardDecl = function(){
	KeyPool.call(this);
	this.name = '';//compulsory
	this.slots = new IdContainer(this.getPool());//slotDecl
	this.chips = new IdContainer(this.getPool());//chipDecl
}
DashboardDecl.prototype.setName = function(dashboard_name){
	this.name = dashboard_name;
	DashPool.add(dashboard_name, this);
}

var SignalDecl = function(){
	this.is_valpush = false;
}

var SlotDecl = function(){//editable slotDecl!
	SignalDecl.call(this);
	this.name = '';//optional
	this.dashes = new StringList();
	this.is_multi = false;
	this.is_exclusive = false;
}
SlotDecl.create = function(key,config){
	var ret = new SlotDecl();
	ret.name = k;
	//TODO
}

var ChipDecl = function(){
	KeyPool.call(this);
	this.name = '';//optional??
	this.feature_name = '';//optional
	this.slots = new IdContainer(this.getPool());
	var inSignal = new SignalDecl();
	var outSignal = new SignalDecl();
	outSignal.is_valpush = true;
	this.slots.add(inSignal);
	this.slots.add(outSignal);
}

var Proto = function(){
	KeyPool.call(this);
	NativeObj.call(this);
	this.objs = new IdContainer(this.getPool());//NativeObj
	this.construct = null;//generated from objs
	this.slots = new IdContainer(this.getPool());//SlotImpl & ValPush
	this.chips = new IdContainer(this.getPool());//ChipImpl
	this.dashImpl = null;
}
Proto.prototype.__proto__ = NativeObj.prototype;
Proto.prototype.createObj = function(){
	//TODO: replicate this.objs
	
	//create SlotInst for all SlotDecl and ChipDecl's SlotDecl
}

/**
 *SlotId is used to identify a slotDecl inside a Proto
*/
var SlotId = function(){
	this.decl_ids = [];
}
SlotId.prototype.getItem = function(proto){
	var ret = proto;
	for (var i=0;i<this.decl_ids.length;i++){
		ret = ret.getItem(this.decl_ids[i]);
	}
	return ret;
}

var Connection = function(){
	this.slotid = new SlotId();
	this.info = 0;//0: two ways, 1: toward, 2:backward
}

var MechanicImpl = function(){
	KeyPool.call(this);
	this.connections = new IdContainer(this.getPool());//connection
}

var SlotImpl = function(){
	MechanicImpl.call(this);
	this.decl = null;//point to slotDecl
	//this.setter = null; //this is for slot_inst
	//this.getter = null; //this is for slot_inst
}
SlotImpl.prototype.__ptoto__ = MechanicImpl.prototype;

var SignalImpl = function(){
	MechanicImpl.call(this);
	this.dir = null;//0: inward, 1: outward
}
SignalImpl.prototype.__ptoto__ = MechanicImpl.prototype;


var ChipImpl = function(){
	this.decl = null;//ChipDecl
	this.slots = null;//SlotImpl & signals
	//this.execute = null;//execute func
}
ChipImpl.prototype.setDecl = function(decl){
	this.decl = decl;
	var slots = decl.slots.get();
	this.slots = {};//clear in case of decl change
	for(k in slots){
		this.slots[k] = new SlotImpl();
		this.slots[k].decl = slots[k];
	}
}
ChipImpl.prototype.getItem = function(key){
	return this.slots[key];
}

var PackagedChip = function(){
	ChipImpl.call(this);
	KeyPool.call(this);
	this.name = '';//compulsory
	this.internal = new IdContainer(this.getPool());//slotImpl & chipImpl
}
PackagedChip.prototype.__proto__ = ChipImpl.prototype;

var ValPush = function(){
	MechanicImpl.call(this);
	this.dashes = new StringList();
	this.val = null;
}
ValPush.prototype.__ptoto__ = MechanicImpl.prototype;
ValPush.prototype.execute = function(){
}


var DNumber = (function(){
	var v;
	return ScalarObj.create({
		name:'Number',
		dash:{//scalar object has only 1 dash
			chips:{//scalar object only has chip!
				'plus':{
					slots:{
						'in':'Number',
						'out':{
							dashes:'Number',
							is_valpush:true
						}
					},
					impl:function(inObj, outSlot){//for scalar, internal obj will be sent instead of the wrapped object
						outSlot.set(inObj + v);
					}
				},
				'greater':{
					slots:{
						'inVal':'Number',
						'outTrue':{
							dashes:[],//singal
							is_valpush:true
						},
						'outFalse':{
							dashes:[],//signal
							is_valpush:true
						}
					},
					impl:function(inObj, outTrue, outFalse){
						if (v>inObj){
							outTrue.set(true);//trigger signal!
						}else{
							outFalse.set(true);//trigger signal!
						}
					}
				}
			}
		},
		getter:function(){//will be called if this is scalar
			return v;
		},
		setter:function(val){//only for scalar
			v = val;
		}
	});
})();

var WebBox = (function(){
	return NativeDashboard.create({
		name:'WebBox',
		slots:{
			'x':(function(){
				var v;
				return {
					dashes:'Number',
					setter:function(val){
						v = val;
						this.get().style.left = v+'px';
					},
					getter:function(){
						return v;
					}
				}
			}()),
			'y':'Number',
			'w':'Number',
			'h':'Number'
		},
		chips:{
			'click':{
				slots:{
					//'in':'WebBox',//first slot is always the this object
					'out':{
						dashes:'WebBox',
						is_valpush: true
					}
				},
				impl:function(outSlot){
					this.get().addEventListener('click',function(e){
						outSlot.set(inObj);
					},false);
				}
			}
		}
	});
}());

var WebDiv = (function(){
	var elem;
	return NativeObj.create({
		name:'WebDiv',
		dashes:[
			'WebBox',
			{
				name:'Div',
				slots:{
					'background':{
						dashes:'String',
						setter:function(val){
							elem.style.background = val;
						},
						getter:function(){
							return elem.style.background;
						}
					}
				},
				chips:{
					'dockChild':{
						slots:{
							'in':'WebBox'
						},
						impl:function(inObj){//parameter will be in order!
							elem.appendChild(inObj.get());
						}
					}
				}
			}
		],
		create:function(){
			elem = document.createElement('div');
			elem.className = 'webdiv';
		},
		destroy:function(){
			elem.parentNode.removeChild(elem);
		},
		getter:function(){
			return elem;
		}
	});
})();


rootload = function(){
	function testArrayProp(){
		var myClass = _inherit([Living], function(){
			this.prop1 = 'abc';
			this.prop2 = 'def';
		});
		ArrayProp(myClass, 'ap');
		ArrayProp(myClass, 'ap2');
		var myObj = new myClass();
		myObj['ap'].push(new Identifiable());
		myObj['ap2'].push(new Identifiable());
		alert(JSON.stringify(myObj['ap'].get()));
		alert(JSON.stringify(myObj['ap2'].get()));
	}
	function testProto(){
		var myGame = Proto.create({
			objs:{
				'WebDiv':{//->key=456
					vals:[//slotid + val
						[
							['WebBox',0],//slot x
							100
						]
					],
					conn:[
						[
							['Div',0],//background
							[[0]]//slot 0
						],
						[
							['Div',1,0],//dockChild in
							[[123]]//webTextBox
						]
					]
				},
				'WebTextBox':{//->key=123
					conn:[
						[
							[],//this object!
							[[1,0]]//chip 1, slot host
						]
					]
				}
			},
			slots:{//slotImpl
				0:{_class:'ValPush',
					val:'red'
				}
			},
			chips:{//chipImpl
				1:{
					id:['WebBox','click'],
					slots:{
						0:[[123]],//0 is host slot, webTextBox
						1:[[2,0]]//chip 2, slot host
					}
				},
				2:{
					id:[],
					slots:{
						0:[1,1]//chip 1, slot out
					}
				}
			},
			dashes:{
				
			}
		});
		myGameObj = {
			objs:{
				456:{
					_class:'WebDiv'
					
				}
			}
		}
		myGame.createObj().execute();
	}
	//testArrayProp();
	//testFilter();

	//sencha touch test
	var carousel = new Ext.Carousel({
		items: [
			{
				html: '<p>Navigate the carousel on this page by swiping left/right.</p>',
			},
			{
				html: '<p>Clicking on either side of the indicators below</p>',
			},
			{
				html: 'Card #3',
			}
		]
	});
	new Ext.Application({
        launch: function() {
			this.viewport = new Ext.Panel({
//				cls: 'cards',
				fullscreen: true,
				layout: {
					type : 'vbox',
					align: 'stretch'
				},
				defaults: {
					flex: 1
				},
				items: [
					carousel,
					{
						xtype    : 'carousel',
						ui       : 'light',
						direction: 'vertical',

						items: [
						{
								html: '<p>Carousels can be vertical and given a ui of "light" or "dark".</p>',
//								cls : 'card card1'
							},
							{
								html: 'Card #2',
//								cls : 'card card2'
							},
							{
								html: 'Card #3',
//								cls : 'card card3'
							}
						]
					}
				]
			});
        }
    });

}
